﻿#include "MaterialGraphViewModel.h"

#include "MaterialGraphModel.h"

#include "Framework/Editor.h"

#include "MaterialGraph/Node/MaterialGraphNodeModel.h"
#include "MaterialGraph/Node/MaterialGraphNodeViewModel.h"

#include "MaterialGraph/Pin/MaterialGraphPinViewModel.h"
#include "MaterialGraph/Pin/MaterialGraphPinModel.h"

#include "MaterialGraph/Link/MaterialGraphLinkModel.h"
#include "MaterialGraph/Link/MaterialGraphLinkViewModel.h"

#include "MaterialGraph/FloatNode/MaterialFloatNodeModel.h"
#include "MaterialGraph/VectorNode/MaterialVectorNodeModel.h"
#include "MaterialGraph/OperationNode/MaterialOperationNodeModel.h"
#include "MaterialGraph/VectorOperationNode/MaterialVectorOperationNodeModel.h"
#include "MaterialGraph/PropertyNode/MaterialPropertyNodeModel.h"
#include "MaterialGraph/SplitVectorNode/MaterialSplitVectorNodeModel.h"
#include "MaterialGraph/TextureNode/MaterialTextureNodeModel.h"
#include "MaterialGraph/CameraNode/MaterialCameraNodeModel.h"
#include "MaterialGraph/BRDFNode/MaterialBRDFNodeModel.h"
#include "MaterialGraph/UnlitNode/MaterialUnlitNodeModel.h"

void RMaterialGraphViewModel::BindModel(TSharedObjectPtr<RMaterialGraphModel> InModel)
{
    GraphModel=InModel;

    for(int64_t i=0; i<GraphModel->GetNodeNum(); i++)
    {
        auto Node=GraphModel->GetNode(i);
        CClass* NodeViewModelClass=REditor::Get()->GetViewModelClass(Node->GetClass());
        auto NodeViewModel=NewObject<RMaterialGraphNodeViewModel>(NodeViewModelClass,this);
        NodeViewModel->BindModel(Node, this);
        NodeViewModels.Add(NodeViewModel);
    }

    GraphModel->OnNodeAdded.Bind([this](int64_t Index){
        auto Node=GraphModel->GetNode(Index);
        CClass* NodeViewModelClass=REditor::Get()->GetViewModelClass(Node->GetClass());
        auto NodeViewModel=NewObject<RMaterialGraphNodeViewModel>(NodeViewModelClass,this);
        NodeViewModel->BindModel(Node, this);
        NodeViewModels.Insert(Index,NodeViewModel);
        OnNodeAdded.ExecuteIfBound(Index);
    });

    GraphModel->OnNodeRemoved.Bind([this](int64_t Index){
        NodeViewModels.RemoveAt(Index);
        OnNodeRemoved.ExecuteIfBound(Index);
    });

    GraphModel->OnSelectedNodeChanged.Bind([this](TSharedObjectPtr<RMaterialGraphNodeModel> Node){
        OnSelectedNodeChanged(Node);
    });
    OnSelectedNodeChanged(GraphModel->GetSelectedNode());

    GraphModel->OnCurrentOutputNodeChanged.Bind([this](TSharedObjectPtr<RMaterialGraphNodeModel> Node){
        OnCurrentOutputNodeChanged(Node);
    });
    OnCurrentOutputNodeChanged(GraphModel->GetCurrentOutputNode());

    for(int64_t i=0; i<GraphModel->GetLinkNum(); i++)
    {
        auto Link=GraphModel->GetLink(i);
        CClass* LinkViewModelClass=REditor::Get()->GetViewModelClass(Link->GetClass());
        auto LinkViewModel=NewObject<RMaterialGraphLinkViewModel>(LinkViewModelClass,this);
        LinkViewModel->BindModel(Link, this);
        LinkViewModels.Add(LinkViewModel);
    }

    GraphModel->OnLinkAdded.Bind([this](int64_t Index){
        auto Link=GraphModel->GetLink(Index);
        CClass* LinkViewModelClass=REditor::Get()->GetViewModelClass(Link->GetClass());
        auto LinkViewModel=NewObject<RMaterialGraphLinkViewModel>(LinkViewModelClass,this);
        LinkViewModel->BindModel(Link, this);
        LinkViewModels.Insert(Index,LinkViewModel);
        OnLinkAdded.ExecuteIfBound(Index);
    });

    GraphModel->OnLinkRemoved.Bind([this](int64_t Index){
        LinkViewModels.RemoveAt(Index);
        OnLinkRemoved.ExecuteIfBound(Index);
    });
}

SVec2 RMaterialGraphViewModel::GetGraphOffset() const
{
    return GraphOffset;
}

void RMaterialGraphViewModel::SetGraphOffset(SVec2 InOffset)
{
    GraphOffset=InOffset;
}


int64_t RMaterialGraphViewModel::GetNodeNum() const
{
    return NodeViewModels.Num();
}

TSharedObjectPtr<RMaterialGraphNodeViewModel> RMaterialGraphViewModel::GetNode(int64_t Index) const
{
    return NodeViewModels[Index];
}

void RMaterialGraphViewModel::AddNode(TSharedObjectPtr<RMaterialGraphNodeViewModel> NodeViewModel)
{
    NodeViewModels.Add(NodeViewModel);
    OnNodeAdded.ExecuteIfBound(NodeViewModels.Num()-1);
}

void RMaterialGraphViewModel::RemoveNode(TSharedObjectPtr<RMaterialGraphNodeViewModel> NodeViewModel)
{
    auto Index=NodeViewModels.Find(NodeViewModel);
    NodeViewModels.RemoveAt(Index.GetValue());
    OnNodeRemoved.ExecuteIfBound(Index.GetValue());
}

int64_t RMaterialGraphViewModel::GetLinkNum() const
{
    return LinkViewModels.Num();
}

TSharedObjectPtr<RMaterialGraphLinkViewModel> RMaterialGraphViewModel::GetLink(int64_t Index) const
{
    return LinkViewModels[Index];
}


TSharedObjectPtr<RMaterialGraphPinViewModel> RMaterialGraphViewModel::FindPinViewModelByModel(TSharedObjectPtr<RMaterialGraphPinModel> InModel)
{
    if(InModel->bIsOutput)
    {
        for(auto PinViewModel : NodeViewModels)
        {
            for(int64_t i=0; i<PinViewModel->GetOutputNum(); i++)
            {
                if(PinViewModel->GetOutput(i)->GetModel()==InModel)
                {
                    return PinViewModel->GetOutput(i);
                }
            }
          
        }
    }
    else
    {
        for(auto PinViewModel : NodeViewModels)
        {
            for(int64_t i=0; i<PinViewModel->GetInputNum(); i++)
            {
                if(PinViewModel->GetInput(i)->GetModel()==InModel)
                {
                    return PinViewModel->GetInput(i);
                }
            }
        }
    }

    assert(false&& "PinViewModel not found");
    return nullptr;
}

void RMaterialGraphViewModel::OnPinDragStarted(TSharedObjectPtr<RMaterialGraphPinViewModel> InPin)
{
    if(InPin->IsOutput())
    {
        SetLinkingStartPin(InPin);
    }
    else
    {
        if(InPin->HasValidLink())
        {
            //remove old link
            auto Link=InPin->BaseModel->GetFirstValidLink();
            GraphModel->RemoveLink(Link);

            //找到这条连接的起点，并开始新的连接
            auto StartPinModel=Link->Start;
            for(auto Node : NodeViewModels)
            {
                for(int64_t i=0; i<Node->GetOutputNum(); i++)
                {
                    if(Node->GetOutput(i)->GetModel()==StartPinModel)
                    {
                        SetLinkingStartPin(Node->GetOutput(i));
                        break;
                    }
                }
            }

        }
        else
        {
            SetLinkingEndPin(InPin);
        };
    }
}

void RMaterialGraphViewModel::OnPinDragEnded(TSharedObjectPtr<RMaterialGraphPinViewModel> InPin)
{
    StopLinking();
}

bool RMaterialGraphViewModel::IsLinking() const
{
    return LinkingStartPin.IsValid() || LinkingEndPin.IsValid();
}

SVec2 RMaterialGraphViewModel::GetLinkingStartPos(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    if(LinkingStartPin.IsValid())
    {
        return InGrapgWidget->PresentSize * 0.5f 
            + SVec2(
                LinkingStartPin->GetCenterXInGraphWidget()
                ,LinkingStartPin->GetCenterYInGraphWidget());
    }

    //try to snap a pin
    auto MousePos= InGrapgWidget->GetMousePositionInWidget();
    auto MousePosInGraphSpace=MousePos-InGrapgWidget->PresentSize*0.5f;
    const float SnapDistance=10;

    auto SelfNode=LinkingEndPin->GetNodeViewModel();

    for(auto Node : NodeViewModels)
    {
        if(Node==SelfNode)
        {
            continue;
        }

        for(int64_t i=0; i<Node->GetOutputNum(); i++)
        {
            auto Pin=Node->GetOutput(i);
            if(Pin->IsHidden())
            {
                continue;
            }

            SVec2 PinPos= SVec2(
                Pin->GetCenterXInGraphWidget()
                ,Pin->GetCenterYInGraphWidget());
            if((PinPos-MousePosInGraphSpace).GetLength()<SnapDistance)
            {
                SnapedPin=Pin;
                return PinPos+InGrapgWidget->PresentSize*0.5f;
            }
        }
    }

    //no pin to snap
    SnapedPin=nullptr;
    return MousePos; 
}

SVec2 RMaterialGraphViewModel::GetLinkingEndPos(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    if(LinkingEndPin.IsValid())
    {
        //正在寻找起点,终点是已知的，直接返回
        return InGrapgWidget->PresentSize * 0.5f 
            + SVec2(
                LinkingEndPin->GetCenterXInGraphWidget()
                ,LinkingEndPin->GetCenterYInGraphWidget());

    }
    
    //try to snap a pin
    auto MousePos= InGrapgWidget->GetMousePositionInWidget();
    auto MousePosInGraphSpace=MousePos-InGrapgWidget->PresentSize*0.5f;
    const float SnapDistance=10;
    
    auto SelfNode=LinkingStartPin->GetNodeViewModel();
    
    for(auto Node : NodeViewModels)
    {
        if(Node==SelfNode)
        {
            continue;
        }
        for(int64_t i=0; i<Node->GetInputNum(); i++)
        {
            auto Pin=Node->GetInput(i);
            if(Pin->IsHidden() || Pin->HasValidLink() )
            {
                continue;
            }
            SVec2 PinPos= SVec2(
                Pin->GetCenterXInGraphWidget()
                ,Pin->GetCenterYInGraphWidget());
            if((PinPos-MousePosInGraphSpace).GetLength()<SnapDistance)
            {
                SnapedPin=Pin;
                return PinPos+InGrapgWidget->PresentSize*0.5f;
            }
        }
    }

    //no pin to snap
    SnapedPin=nullptr;
    return MousePos;
}

SColor RMaterialGraphViewModel::GetLinkingStartColor() const
{
    if(LinkingStartPin.IsValid())
    {
        return LinkingStartPin->GetPinColor();
    }
    else if(LinkingEndPin.IsValid())
    {
        return LinkingEndPin->GetPinColor();
    }

    assert(false&&"只有正在连接时才有颜色");
    return SColor();

}

SColor RMaterialGraphViewModel::GetLinkingEndColor() const
{
    if(LinkingEndPin.IsValid())
    {
        return LinkingEndPin->GetPinColor();
    }
    else if(LinkingStartPin.IsValid())
    {
        return LinkingStartPin->GetPinColor();
    }

    assert(false&&" no linking pin");
}

void RMaterialGraphViewModel::AddFloatNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialFloatNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddVectorNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialVectorNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddOperationNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialOperationNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddVectorOperationNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialVectorOperationNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddSplitVectorNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialSplitVectorNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddTextureNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialTextureNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddCameraNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialCameraNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddDelayBRDFNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialBRDFNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddUnlitNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialUnlitNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddPropertyNodeAtMouse(TSharedObjectPtr<RWidget> InGrapgWidget)
{
    AddNodeAtMouse<RMaterialPropertyNodeModel>(InGrapgWidget);
}

void RMaterialGraphViewModel::AddOperation(TSharedObjectPtr<RMaterialEditOperation> Operation)
{
    GraphModel->AddOperation(Operation);
}

void RMaterialGraphViewModel::RedoOperation(TSharedObjectPtr<RMaterialEditOperation> Operation)
{
    Operation->Redo(GraphModel);
}

void RMaterialGraphViewModel::SelectNode(TSharedObjectPtr<RMaterialGraphNodeViewModel> NodeViewModel)
{
    if(!NodeViewModel)
    {
        GraphModel->SetSelectedNode(nullptr);
    }
    else
    {
        GraphModel->SetSelectedNode(NodeViewModel->GetBaseNodeModel());
    }
}

void RMaterialGraphViewModel::OnSelectedNodeChanged(TSharedObjectPtr<RMaterialGraphNodeModel> InNodeModel)
{
    if(!InNodeModel)
    {
        SelectedNodeViewModel=nullptr;
    }
    else
    {
        for(auto Node : NodeViewModels)
        {
            if(Node->GetBaseNodeModel()==InNodeModel)
            {
                SelectedNodeViewModel=Node;
                break;
            }
        }
    }
}

bool RMaterialGraphViewModel::IsNodeSelected(TSharedObjectPtr<RMaterialGraphNodeViewModel> NodeViewModel) const
{
    return SelectedNodeViewModel==NodeViewModel;
}

void RMaterialGraphViewModel::DeleteSelectedNode()
{
    GraphModel->RemoveSelectedNode();
}

void RMaterialGraphViewModel::OnCurrentOutputNodeChanged(TSharedObjectPtr<RMaterialGraphNodeModel> InNodeModel)
{
    if(!InNodeModel)
    {
        CurrentOutputNodeViewModel=nullptr;
    }
    else
    {
        for(auto Node : NodeViewModels)
        {
            if(Node->GetBaseNodeModel()==InNodeModel)
            {
                CurrentOutputNodeViewModel=Node;
                break;
            }
        }
    }
}

bool RMaterialGraphViewModel::IsCurrentOutputNode(TSharedObjectPtr<RMaterialGraphNodeViewModel> NodeViewModel) const
{
    return CurrentOutputNodeViewModel==NodeViewModel;
}

void RMaterialGraphViewModel::SetLinkingStartPin(TSharedObjectPtr<RMaterialGraphPinViewModel> Pin)
{
    LinkingStartPin=Pin;
}

void RMaterialGraphViewModel::SetLinkingEndPin(TSharedObjectPtr<RMaterialGraphPinViewModel> Pin)
{
    LinkingEndPin=Pin;
}

void RMaterialGraphViewModel::StopLinking()
{
    if(SnapedPin)
    {
        //create link
        if(LinkingStartPin)
        {
            GraphModel->CreateAndAddLink(
                LinkingStartPin->GetModel()
                ,SnapedPin->GetModel()
                );
        }
        else
        {
            GraphModel->CreateAndAddLink(
                SnapedPin->GetModel()
                ,LinkingEndPin->GetModel()
                );
        }
    }

    LinkingStartPin=nullptr;
    LinkingEndPin=nullptr;
    SnapedPin=nullptr;
}
